package com.onpositive.data.serializer.readers;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Date;

import com.onpositive.data.IBaseReader;
import com.onpositive.data.IReadableObjectCollection;
import com.onpositive.data.ISeekableAccess;
import com.onpositive.data.diskstorage.StoredCollection;
import com.onpositive.data.diskstorage.StoredMetadata;
import com.onpositive.data.serializer.writers.ObjectCollectionWriter;

public class ObjectCollectionReader implements IBaseReader {

	private static final int INT_SIZE = 4 ;
	
	@Override
	public IReadableObjectCollection<?>[] getCollection(ISeekableAccess rf) {
		
		int size ;
		
		byte[] offsetsByteArray ;
		try {
			rf.seek( INT_SIZE ) ;//skip id ;
			size = rf.readInt() ;
			
			if( size <= 0 )
				return null ;
			
			offsetsByteArray = new byte[ INT_SIZE *( size + 1 ) ] ;
			rf.read( offsetsByteArray, 0, INT_SIZE *( size + 1 ) ) ;
			
		} catch (IOException e) {
			e.printStackTrace();
			return null ;
		}
		
		ByteBuffer bBuf = ByteBuffer.wrap( offsetsByteArray ) ;
		IntBuffer  iBuf = bBuf.asIntBuffer() ;
		//this.offsets = iBuf.array() ;
		
		IReadableObjectCollection<?> result[] = new IReadableObjectCollection[ size ] ;
		for( int i = 0 ; i < size ; i++ )
			result[i] = readCollection( rf, iBuf.get(i), iBuf.get(i+1)) ;
					
		return result;
	}
	
	private StoredCollection readCollection( ISeekableAccess rf , int offStart, int offTerminate ){
		
		int size ;
		int propertiesCount ;
		byte[] offsetsByteArray ;
		try {
			rf.seek( offStart ) ;
			size = rf.readInt() ;
			propertiesCount = rf.readInt() ;
			
			if( propertiesCount <= 0 )
				return null ;
			
			offsetsByteArray = new byte[ INT_SIZE *( propertiesCount + 1 ) ] ;
			rf.read( offsetsByteArray, 0, INT_SIZE *( propertiesCount + 1 ) ) ;
			
		} catch (IOException e) {
			e.printStackTrace();
			return null ;
		}
		
		ByteBuffer bBuf = ByteBuffer.wrap( offsetsByteArray ) ;
		IntBuffer  iBuf = bBuf.asIntBuffer() ;
		
		StoredCollection result = new StoredCollection( rf, offStart, offTerminate, size ) ;
	
		for( int i = 0 ; i < propertiesCount ; i++ )
			result.addMetadata( readMetadata(  rf, iBuf.get(i), iBuf.get(i+1), offStart ) ) ;
		
		try {
			rf.seek( offStart + iBuf.get( propertiesCount ) ) ;
			rf.read( offsetsByteArray, 0, INT_SIZE *( propertiesCount + 1 ) ) ;
			bBuf = ByteBuffer.wrap( offsetsByteArray ) ;
			iBuf = bBuf.asIntBuffer() ;
			
			int[] dataOffsets = new int[ propertiesCount + 1 ] ;
			for( int i = 0 ; i < propertiesCount + 1 ; i++ )
				dataOffsets[i] = iBuf.get() ;
			
			result.setDataOffsets( dataOffsets ) ;
			
		} catch (IOException e) {
			e.printStackTrace();
			return null ;
		}
		
		return result ;
	}
	
	private StoredMetadata readMetadata( ISeekableAccess rf , int offStart, int offTerminate, int collectionStartOffset ){
		
		StoredMetadata result = new StoredMetadata() ;
		
		try {
			rf.seek( offStart + collectionStartOffset ) ;
			String id = rf.readUTF() ;
			result.setId(id) ;
			
			int type = rf.readInt() ;
			result.setType(type) ;
			
			int keysCount = rf.readInt() ;
			
			for( int i = 0 ; i < keysCount ; i++ )
			{
				String key = rf.readUTF() ;
				Object value = readUntypedValue( rf ) ;
				result.setVlaue(key, value) ;
			}
			
		} catch (IOException e1) {
			e1.printStackTrace();
			return null ;
		}		
		return result ;
	}

	private Object readUntypedValue( ISeekableAccess rf ) throws IOException
	{
		int typeCode ;
		
		typeCode = rf.readInt(); 
			
		if ( typeCode == ObjectCollectionWriter.TYPE_NULL_ID )
			return null;
			
		if ( typeCode == ObjectCollectionWriter.TYPE_STRING_ID )
			return rf.readUTF();
				
		if ( typeCode == ObjectCollectionWriter.TYPE_INTEGER_ID )
			return rf.readInt() ;
	
		if ( typeCode == ObjectCollectionWriter.TYPE_DOUBLE_ID )
			return rf.readDouble() ;
		
		if ( typeCode == ObjectCollectionWriter.TYPE_FLOAT_ID )
			return rf.readFloat() ;
				
		if ( typeCode == ObjectCollectionWriter.TYPE_DATE_ID )
			return new Date( rf.readLong() ) ;
	
		if ( typeCode == ObjectCollectionWriter.TYPE_BOOLEAN_ID )
			return rf.readBoolean() ;			
		
		return null ;
	}
	
	

}
